Previous: Business Days, Up: Date Arithmetic [Contents][Index]
Time zones and daylight saving time are a complicated business. The conversions to and from Julian and Unix-style dates automatically compute the correct time zone and daylight saving adjustment to use, provided they can figure out this information. This section describes Calc’s time zone adjustment algorithm in detail, in case you want to do conversions in different time zones or in case Calc’s algorithms can’t determine the right correction to use.
Adjustments for time zones and daylight saving time are done by t U, t J, t N, and t C, but not by any other commands. In particular, ‘<may 1 1991> - <apr 1 1991>’ evaluates to exactly 30 days even though there is a daylight-saving transition in between. This is also true for Julian pure dates: ‘julian(<may 1 1991>) - julian(<apr 1 1991>)’. But Julian and Unix date/times will adjust for daylight saving time: using Calc’s default daylight saving time rule (see the explanation below), ‘julian(<12am may 1 1991>) - julian(<12am apr 1 1991>)’ evaluates to ‘29.95833’ (that’s 29 days and 23 hours) because one hour was lost when daylight saving commenced on April 7, 1991.
In brief, the idiom ‘julian(date1) - julian(date2)’ computes the actual number of 24-hour periods between two dates, whereas ‘date1 - date2’ computes the number of calendar days between two dates without taking daylight saving into account.
The calc-time-zone [tzone] command
converts the time zone specified by its numeric prefix argument
into a number of seconds difference from Greenwich mean time
(GMT). If the argument is a number, the result is simply that
value multiplied by 3600. Typical arguments for North America are
5 (Eastern) or 8 (Pacific). If Daylight Saving time is in effect,
one hour should be subtracted from the normal difference.
If you give a prefix of plain C-u,
calc-time-zone (like other date arithmetic commands
that include a time zone argument) takes the zone argument from
the top of the stack. (In the case of t J and t
U, the normal argument is then taken from the second-to-top
stack position.) This allows you to give a non-integer time zone
adjustment. The time-zone argument can also be an HMS form, or it
can be a variable which is a time zone name in upper- or
lower-case. For example ‘tzone(PST) =
tzone(8)’ and ‘tzone(pdt) =
tzone(7)’ (for Pacific standard and daylight saving
times, respectively).
North American and European time zone names are defined as follows; note that for each time zone there is one name for standard time, another for daylight saving time, and a third for “generalized” time in which the daylight saving adjustment is computed from context.
YST PST MST CST EST AST NST GMT WET MET MEZ 9 8 7 6 5 4 3.5 0 -1 -2 -2 YDT PDT MDT CDT EDT ADT NDT BST WETDST METDST MESZ 8 7 6 5 4 3 2.5 -1 -2 -3 -3 YGT PGT MGT CGT EGT AGT NGT BGT WEGT MEGT MEGZ 9/8 8/7 7/6 6/5 5/4 4/3 3.5/2.5 0/-1 -1/-2 -2/-3 -2/-3
To define time zone names that do not appear in the above
table, you must modify the Lisp variable
math-tzone-names. This is a list of lists describing
the different time zone names; its structure is best explained by
an example. The three entries for Pacific Time look like
this:
( ( "PST" 8 0 ) ; Name as an upper-case string, then standard ( "PDT" 8 -1 ) ; adjustment, then daylight saving adjustment. ( "PGT" 8 "PST" "PDT" ) ) ; Generalized time zone.
With no arguments, calc-time-zone or
‘tzone()’ will by default get the time
zone and daylight saving information from the calendar (see
The Calendar and the Diary in The GNU Emacs
Manual). To use a different time zone, or if the calendar
does not give the desired result, you can set the Calc variable
TimeZone (which is by default nil) to
an appropriate time zone name. (The easiest way to do this is to
edit the TimeZone variable using Calc’s s
T command, then use the s p
(calc-permanent-variable) command to save the value
of TimeZone permanently.) If the time zone given by
TimeZone is a generalized time zone, e.g.,
EGT, Calc examines the date being converted to tell
whether to use standard or daylight saving time. But if the
current time zone is explicit, e.g., EST or
EDT, then that adjustment is used exactly and
Calc’s daylight saving algorithm is not consulted. The
special time zone name local is equivalent to no
argument; i.e., it uses the information obtained from the
calendar.
The t J and t U commands with no
numeric prefix arguments do the same thing as
‘tzone()’; namely, use the information
from the calendar if TimeZone is nil,
otherwise use the time zone given by
TimeZone.
When Calc computes the daylight saving information itself
(i.e., when the TimeZone variable is set), it will
by default consider daylight saving time to begin at 2 a.m. on
the second Sunday of March (for years from 2007 on) or on the
last Sunday in April (for years before 2007), and to end at 2
a.m. on the first Sunday of November. (for years from 2007 on) or
the last Sunday in October (for years before 2007). These are the
rules that have been in effect in much of North America since
1966 and take into account the rule change that began in 2007. If
you are in a country that uses different rules for computing
daylight saving time, you have two choices: Write your own
daylight saving hook, or control time zones explicitly by setting
the TimeZone variable and/or always giving a
time-zone argument for the conversion functions.
The Lisp variable math-daylight-savings-hook
holds the name of a function that is used to compute the daylight
saving adjustment for a given date. The default is
math-std-daylight-savings, which computes an
adjustment (either 0 or -1) using the North American rules
given above.
The daylight saving hook function is called with four
arguments: The date, as a floating-point number in standard Calc
format; a six-element list of the date decomposed into year,
month, day, hour, minute, and second, respectively; a string
which contains the generalized time zone name in upper-case,
e.g., "WEGT"; and a special adjustment to be applied
to the hour value when converting into a generalized time zone
(see below).
The Lisp function math-prev-weekday-in-month is
useful for daylight saving computations. This is an internal
version of the user-level pwday function described
in the previous section. It takes four arguments: The
floating-point date value, the corresponding six-element date
list, the day-of-month number, and the weekday number
(0–6).
The default daylight saving hook ignores the time zone name, but a more sophisticated hook could use different algorithms for different time zones. It would also be possible to use different algorithms depending on the year number, but the default hook always uses the algorithm for 1987 and later. Here is a listing of the default daylight saving hook:
(defun math-std-daylight-savings (date dt zone bump)
(cond ((< (nth 1 dt) 4) 0)
((= (nth 1 dt) 4)
(let ((sunday (math-prev-weekday-in-month date dt 7 0)))
(cond ((< (nth 2 dt) sunday) 0)
((= (nth 2 dt) sunday)
(if (>= (nth 3 dt) (+ 3 bump)) -1 0))
(t -1))))
((< (nth 1 dt) 10) -1)
((= (nth 1 dt) 10)
(let ((sunday (math-prev-weekday-in-month date dt 31 0)))
(cond ((< (nth 2 dt) sunday) -1)
((= (nth 2 dt) sunday)
(if (>= (nth 3 dt) (+ 2 bump)) 0 -1))
(t 0))))
(t 0))
)
The bump parameter is equal to zero when Calc is
converting from a date form in a generalized time zone into a GMT
date value. It is -1 when Calc is converting in the other
direction. The adjustments shown above ensure that the conversion
behaves correctly and reasonably around the 2 a.m. transition in
each direction.
There is a “missing” hour between 2 a.m. and 3 a.m. at the beginning of daylight saving time; converting a date/time form that falls in this hour results in a time value for the following hour, from 3 a.m. to 4 a.m. At the end of daylight saving time, the hour from 1 a.m. to 2 a.m. repeats itself; converting a date/time form that falls in this hour results in a time value for the first manifestation of that time (not the one that occurs one hour later).
If math-daylight-savings-hook is
nil, then the daylight saving adjustment is always
taken to be zero.
In algebraic formulas, ‘tzone(zone, date)’ computes the time zone adjustment for a given zone name at a given date. The date is ignored unless zone is a generalized time zone. If date is a date form, the daylight saving computation is applied to it as it appears. If date is a numeric date value, it is adjusted for the daylight-saving version of zone before being given to the daylight saving hook. This odd-sounding rule ensures that the daylight-saving computation is always done in local time, not in the GMT time that a numeric date is typically represented in.
The ‘dsadj(date,
zone)’ function computes the daylight
saving adjustment that is appropriate for date in time
zone zone. If zone is explicitly in or not
in daylight saving time (e.g., PDT or
PST) the date is ignored. If
zone is a generalized time zone, the algorithms
described above are used. If zone is omitted, the
computation is done for the current time zone.
Previous: Business Days, Up: Date Arithmetic [Contents][Index]